using UnityEngine;
using System.Collections;

public enum Stone
{
	black=0,
	white=1,
	legal=2,
	empty=3,
	edge=4
}


public class boardManager : MonoBehaviour 
{
	

	public Material[] piece;
	public int localPlayer = (int)Stone.black;

	private int[] Squares;
	private int currentPlayer = (int)Stone.black;
	private int currentLegalMoves = 0;
	private bool boardFullStart = false;  
	private gameManager GM;
	
	private int bestComputerMove = 11;
	private int bestComputerMoveStones = 0;
	
	private float nextComputerClickTime;
	private float computerClickDelay = 2;
	
	public int currentWhiteStones = 0;
	public int currentBlackStones = 0;
	
	void Start()
	{
		GM = GameObject.Find("World").GetComponent<gameManager>();
		if(!GM)
		{
			Debug.LogError("gameManager not found.  Add a gameManager to World.");
		}
	}
	
	// Check for user input
		
	void Update()
	{
			
		bool AcceptLocal=false;
		
		// Hot Seat: Accept mouseclicks if we are in play mode for either side
		if(GM.gameType==(int)Type.hotseat)
		{
			if(GM.currentState==(int)State.playblack || GM.currentState==(int)State.playwhite)
				AcceptLocal=true;
		}
		// Network Multiplayer: Accept mouseclick only if currentPlayer matches localPlayer
		if(GM.gameType==(int)Type.multi)
		{
			if(GM.currentState==localPlayer)
				AcceptLocal=true;
		}
		
		// Single Player: Accept mouseclicks when it is the player's turn, otherwise let the computer take its turn
		// Currenlty, in a single player game, the human player is always black
		if(GM.gameType==(int)Type.single)
		{
			if(GM.currentState==(int)State.playblack)
			{
				AcceptLocal=true;
				
			}
			
			if(GM.currentState==(int)State.playwhite)
			{
				
				if(Time.realtimeSinceStartup > nextComputerClickTime)
				{
					ClickLegal(bestComputerMove);
					
					// set this to a minimum point in time ahead of now
					// this will get set again after the player clicks
					// if there is a human player
					nextComputerClickTime = Time.realtimeSinceStartup + computerClickDelay;
				}
				
			}
		}
		
		if( AcceptLocal && Input.GetMouseButtonDown(0))
		{
			
			RaycastHit hit;
			Ray ray  = Camera.main.ScreenPointToRay(Input.mousePosition);
        
        	if (Physics.Raycast(ray, out hit)){
				
				// because the size of the planes is 10, we can calcuate the
				// the array index from the transform position
				
				int index = (int)hit.transform.position.z + Mathf.FloorToInt((hit.transform.position.x * 0.1F));
				ClickLegal(index);
				nextComputerClickTime = Time.realtimeSinceStartup + computerClickDelay;
				
				// if this is a multiplayer game then send the click to the other player
				
				if(GM.gameType==(int)Type.multi)
				{
					if(Network.isServer)
					{
						// send to the client
						GM.SendClick(RPCMode.Others, index);
						
					} else {
						// send to the server
						GM.SendClick(RPCMode.Server, index);
						
					}
				}
					
			}
	
		}
		
		
		// accept the Escape Key in any mode
		// the escape key does different things
		// depending on which game state we are in
		// so ask the state manager to process
		// a "state escape"
		
		if (Input.GetKeyDown(KeyCode.Escape)){
	 		GM.EscapeState();
	 	}
		
	}
	
	[RPC]
	public void ClickLegal(int index)
	{
		
		SetSquareState(index,currentPlayer);
		ClearLegalMoves();
		CapturePieces(index);
		currentPlayer = 1-currentPlayer;
		currentLegalMoves = FindLegalMoves(currentPlayer);
		
		// the current player id matches the "play" state id
		// for that player, so the currentPlayer can be used
		// to set the play state
		
		GM.ReplaceState(currentPlayer);
		
		// evaluate if the game is over because there were no legal moves found
		// FindLegalMoves() will also return 0 if the board is full

		if(currentLegalMoves == 0)
		{
			ChooseWinner();
		}

		
	}
	[RPC]
	public void SetLocalPlayer(int i)
	{
		localPlayer = i;
	}
	
	public void StartNewBoard()
	{
		ResetBoard();
		if(boardFullStart)
		{
			AddEndGameStones();
			
		} else {
			
			AddStartingStones();
			
		}
		currentPlayer = (int)Stone.black;
		currentLegalMoves = FindLegalMoves(currentPlayer);
	}

	void ChooseWinner()
	{
		int blackStones = 0;
		int whiteStones = 0;
		for (int i=0; i<Squares.Length; i++)
		{
			if(Squares[i] == (int)Stone.black)
				blackStones++;
			if(Squares[i] == (int)Stone.white)
				whiteStones++;
		}
		
		GM.ReplaceState((int)State.tiegame);
				
		if(blackStones > whiteStones)
		{
			GM.ReplaceState((int)State.blackwon);
		}else{
			GM.ReplaceState((int)State.whitewon);
		}
		
	}
	

	int FindLegalMoves(int player)
	{
		// find all the legal moves for
		// a player by looking at each
		// square, if it is owned by the player
		// then look in all 8 directions to
		// see if there is at least 1 opponent-owned
		// square and then an empty square --
		// that empty square becomes a legal square
		
		float startSearch = Time.realtimeSinceStartup;
		int movesFound = 0;
		bestComputerMoveStones = 0;
		bestComputerMove = 11;
		currentWhiteStones = 0;
		currentBlackStones = 0;

		for (int i=0; i<Squares.Length; i++)
		{
			if(Squares[i] == (int)Stone.black)
				currentBlackStones++;
			
			if(Squares[i] == (int)Stone.white)
				currentWhiteStones++;
			
			if(Squares[i] == player)
			{
				for (int dy=-1; dy<2; dy++)
				{
					for (int dx=-1;dx<2; dx++)
					{
						if ((dx != 0) | (dy != 0))
						{
							bool stopSearching = false;
							bool primed = false;
							int j = i;
							int k = 0;
							while (stopSearching == false)
							{
								j+=(dy*10)+dx;
								k++;
								if((Squares[j]!=(1-player)))
								{
									stopSearching = true;
									
								} else {
									
									if(Squares[j]==(1-player)){
										// an empty space cannot be
										// considered legal until
										// at least 1 opponent is
										// found
										primed = true;
										
									}
								}
								if((Squares[j]==(int)Stone.empty) && (primed==true))
								{
									SetSquareState(j,(int)Stone.legal);
									movesFound++;
									Debug.Log(j.ToString() + " legal move for " + i.ToString());
									if(k > bestComputerMoveStones)
									{
										bestComputerMoveStones = k;
										bestComputerMove = j;
									}
								}
								
							} 
						}
					}
				}
			}
		}
		
		Debug.Log("======>Find Legal Moves Search Time: " + (Time.realtimeSinceStartup -startSearch).ToString());
		return movesFound;
	}
	
	void CapturePieces(int index)
	{
		// starting at the selected square
		// search in all 8 directions for
		// squares that match the color of
		// the selected square
		// if found, flip the current direction
		// and change all opponent stones to
		// selected square's color
		
		int player = Squares[index];
		float startSearch = Time.realtimeSinceStartup;

		for (int dy=-1; dy<2; dy++)
		{
			for (int dx=-1;dx<2; dx++)
			{
				if ((dx != 0) | (dy != 0))
				{
					bool stopSearching = false;
					bool primed = false;
					int j = index;
					while (stopSearching == false)
					{
						j+=(dy*10)+dx;
						if((Squares[j]!=(1-player)))
						{
							stopSearching = true;
							
						} else {
							
							if(Squares[j]==(1-player)){
								primed = true;
								
							}
						}
						if((Squares[j]==player) && (primed==true))
						{
							Debug.Log("Capture Sequence found from "+index.ToString() + " to " + j.ToString());
							
							j = index;
							stopSearching = false;
							while (stopSearching == false)
							{
								j+=(dy*10)+dx;
								if(Squares[j]!=(1-player))
								{
									stopSearching = true;
									
								} else {
									
									SetSquareState(j,player);
									
								}
							}
						}
						
					} 
				}
			}
		}
		
				
		Debug.Log("=====>Capture Pieces Search Time: " + (Time.realtimeSinceStartup -startSearch).ToString());
	}

	
	void ClearLegalMoves()
	{
		// set the open squares that were legal
		// back to empty - this is so that the
		// legal moves for one player do not become
		// the legal moves for the other player
		
		for (int i=0; i<Squares.Length; i++)
		{
			// if the square is legal, set it to empty
			
			if(Squares[i]==(int)Stone.legal)
			{
				SetSquareState(i, (int)Stone.empty);
				
			}
			
		}

	}
		
	void ResetBoard()
	{
		// create a new array of squares for the board
		Squares = new int[100];
		
		for (int i=0; i<Squares.Length; i++)
		{
			
			// set the squares to be either 
			// empty stones or edges
			
			if((i<10) | (i>89) | (((i+1)%10)<2))
			{
				SetSquareState(i, (int)Stone.edge);
				
			} else {
				
				SetSquareState(i, (int)Stone.empty);
			}
			
		}
		
				
	}
	
	void SetSquareState(int i, int s)
	{
		GameObject Square;
		
		Square = GameObject.Find(("Square"+i.ToString()));
		
		// if the square plane does not exit
		// then create it - this will
		// only need to be done once
		// per square plane
		
		if(Square == null)
		{

			Square = GameObject.CreatePrimitive(PrimitiveType.Plane);
			int xpos = Mathf.FloorToInt(i%10) * Mathf.FloorToInt(Square.renderer.bounds.size.x);
			int zpos = Mathf.FloorToInt(i/10) * Mathf.FloorToInt(Square.renderer.bounds.size.z);
			Square.transform.position = new Vector3(xpos,0,zpos);
			Square.name = ("Square"+i.ToString());

		}
		
		// update the Squares array
		Squares[i] = s;
			
		// update the material of the square to match its square state
		Square.renderer.sharedMaterial = piece[(Squares[i])];
		
		// if the stone state is legal, then turn on its collider
		// otherwise turn the collider off
		// a collider that is turned off will 
		// not be able to receive the raycast
		// from mouse clicks -- only legal moves are allowed
		
		if(Squares[i] == (int)Stone.legal)
		{
			Square.collider.enabled = true;
			
		} else {
			
			Square.collider.enabled = false;
			
		}
		
		
	}
	
	
	void AddStartingStones()
	{
		SetSquareState(44,(int)Stone.black);
		SetSquareState(45,(int)Stone.white);
		SetSquareState(54,(int)Stone.white);
		SetSquareState(55,(int)Stone.black);	               
	}
	
	void AddEndGameStones()
	{
		// this is a debug function that will
		// put the game board
		// into a state where only 1 move is 
		// required to end the game - used for
		// testing end game logic
		
		for (int i=0; i<Squares.Length; i++)
		{
			
			// set the squares to be either 
			// white stones or edges
			
			if((i<10) | (i>89) | (((i+1)%10)<2))
			{
				SetSquareState(i, (int)Stone.edge);
				
			} else {
				
				SetSquareState(i, (int)Stone.white);
			}
			
		}
		
		SetSquareState(11,(int)Stone.empty);
		SetSquareState(88,(int)Stone.black);

		
	}
	
	
	void AddRandomStones(int stones)
	{
		// this is a debug function that will
		// add some random stones to the board
		// for either player, this is used
		// to test the FindLegalMoves logic,
		// so it doesn't have to make the  
		// total number of stones for each
		// player be the same
		
		for (int i=0; i<stones; i++)
		{
			// use only values 1 through 8 to
			// make sure that we don't put any
			// stones on the edges
			// Unity's Random.Range function
			// can never return
			// the max value, so we use a max
			// value of (desired max value + 1)
			
			int x=Random.Range(1,9);
			int y=Random.Range(1,9);
			int s=Random.Range(0,2);
			Squares[((y*10)+x)]=s;
		}
	}
	
	void PrintBoard(string msg)
	{
		// print out the board to the log
		// in a way that's easy to read
		
		string s = msg + "\n";
		int i = 0;
		for (i=0; i<Squares.Length; i++)
		{
			s+=Squares[i].ToString();
			if (((i+1)%10)==0)
			{
				
				s+=" : " + i.ToString()+"\n";
			}
		}
		
		Debug.Log(s);
	}
			
}
